home *** CD-ROM | disk | FTP | other *** search
/ CD PowerPlay 10 / CD Powerplay Issue 10 (February 1996).iso / hints / onefall / omfutl / utilscom / afhack.c next >
Encoding:
C/C++ Source or Header  |  1995-09-09  |  10.6 KB  |  352 lines

  1. const char *Version=
  2. "AFHack Version 1.1 - AF hacking utility for One Must Fall 2097 .AF files\n"
  3. "By Kenneth F. Henderson Jr. (71672,1777)";
  4. /*
  5.    vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  6.  
  7. >> BACK UP YOUR ORIGINAL .AF FILES BEFORE USING THIS PROGRAM! <<
  8.  
  9.    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  10.  
  11.  This code is, of course, provided AS IS, with no waranty or anything else.
  12. Copy it, rewrite it, use it, FUBAR it, whatever you like (I would prefer that
  13. you include the above credits if you redistribute it, BTW), however no matter
  14. what happens none of the aforementioned people can be held responsible, whether
  15. your hard disk crashes, your computer dies, OMF decides to cheat on you every
  16. time you play, or anything else.  <grin>
  17.  
  18.  WARNING: The Borland C++ project file that accompanies this code turns
  19. 386 instructions ON, on the premise that since OMF requires a 386, all
  20. machines running this code will have at least a 386.  The included .COM file
  21. was compiled using this project file, so it may very well hang (or worse)
  22. on a lesser machine unless recompiled with the option set down to whatever
  23. you're using.
  24.  
  25.  Before use, you *must* backup your .AF files.  Personally, I recommend
  26. creating a subdirectory named "s" under your main OMF directory and copying
  27. *.AF into this new subdirectory.  Then use the original .AFs in that
  28. subdirectory as the *SOURCE* .AF files with this utility, and use the ones in
  29. your main OMF directory as the *DESTINATION*.  Make certain you don't ever
  30. overwrite the backups, or you'll have to reinstall OMF to restore the
  31. original operation of the 'bots.  Running this program completely overwrites
  32. the destination AF file, but does not modify the hack file or the source AF.
  33.  
  34.  For basic usage instructions, just run the program with no parameters.
  35.  
  36.  If you just want to use this program to patch in moves created by others, you
  37. probably know all you need to know about this utility now.  However, if you
  38. want to make your own moves using this utility, and you understand the .AF
  39. format well enough to write move strings, read on.
  40.  
  41.  This utility uses script files to describe moves for patching in.  The format
  42. is very simple - any line which isn't part of the script is a comment, and
  43. *MUST* start with a semicolon (';'), unless you enjoy error messages.  ;-)
  44.  The first non-comment line in the file needs to be the number of hacks in the
  45. file.  This is used as a checksum of sorts, and also makes sure the utility
  46. doesn't read any further than it needs to.  (In fact, you could put comments
  47. without semicolons after the last hack, since, as long as the number of hacks
  48. is right, the utility will never get that far.)
  49.  All non-comment lines after that (up to the number of hacks specified, as
  50. mentioned above) need to be in the following format, and in address order.
  51.  
  52. AAAAAAAA:T:AF-String
  53.  
  54.  The 'A's should be the up-to-eight-digit *hexadecimal* file offset of the
  55. move you want to change.  That is, the address of the first letter of the
  56. move.  You can find this with the string finder included with this package,
  57. assuming you know what to look for (the keystroke sequences nearby are usually
  58. enough to tell you which string you're looking for.)
  59.  The 'T' (type of patch) should be either an 'R' or an 'O'.  'R' is for
  60. automatic resizing - the space for the existing string will automatically be
  61. resized to fit the new string being patched in.  This is intended only for use
  62. with the actual move strings, and may or may not work if you use it to modify
  63. other parts of the file.  'O' is used for directly overwriting the specified
  64. part of the file - good for key strings and the like.
  65.  'AF-String' is the whole string you wish to patch in.  Don't use newlines in
  66. the string, or you'll confuse the utility.  You may need a pretty good text
  67. editor to make long strings; most editors have some sort of limit on line
  68. length.  If your editor can handle lines up to 1024 characters (which is about
  69. as much as OMF itself can take), you're all set.  If not... get yourself a
  70. better editor!  <g>
  71.  
  72. Below is the actual C source code.  Hackers only.  B-)
  73. */
  74. #include <stdio.h>
  75. #include <stdlib.h>
  76. #include <stdarg.h>
  77. #include <string.h>
  78. #include <alloc.h>
  79.  
  80. //  Return codes (RET_)
  81. //These codes may change from version to version
  82. //If you use them, always check for changes when you get a new version
  83. #define    RET_Normal    0
  84. #define    RET_BadParms    -1
  85. #define    RET_BadFile    -2
  86. #define    RET_BadRead    -3
  87. #define    RET_BadClose    -4
  88. #define    RET_OutOfMemory    -5
  89.  
  90. int    HackScanf(FILE *FileHack,const char *format, ...)
  91. {
  92.     va_list    args;
  93.     int    i;
  94.  
  95.     //Skip comments
  96.     while( (i=getc(FileHack)) == ';' )
  97.         while( getc(FileHack)!='\n' && !feof(FileHack) );
  98.     ungetc(i,FileHack);
  99.  
  100.     va_start(args,format);
  101.     i=vfscanf(FileHack,format,args);
  102.     va_end(args);
  103.     return i;
  104. }
  105.  
  106. int    ParseCString(char *String)
  107. {
  108.     char    *S,*D;
  109.  
  110.     //Check for first occurence; no occurence = nothing to do
  111.     if( !(S=D=strchr(String,'\\')) )
  112.         return strlen (String);
  113.  
  114. //Loop until end of string, skip opening '\'.  On last pass, S will still be
  115. //incremented again, but it doesn't matter since S isn't used after this
  116.     while(*(S++))
  117.     {
  118.         switch(*S)
  119.         {
  120.             case 'a':    *(D++)='\a';break;
  121.             case 'b':    *(D++)='\b';break;
  122.             case 'f':    *(D++)='\f';break;
  123.             case 'n':    *(D++)='\n';break;
  124.             case 'r':    *(D++)='\r';break;
  125.             case 't':    *(D++)='\t';break;
  126.             case 'v':    *(D++)='\v';break;
  127.             case '\\':    *(D++)='\\';break;
  128.             case '\'':    *(D++)='\'';break;
  129.             case '\"':    *(D++)='\"';break;
  130.             case '\?':    *(D++)='\?';break;
  131. //Numeric support hardcoded for 0 only; saves trouble.  Maybe add later if requested.
  132.             case '0':    *(D++)='\0';break;
  133.             case 'x':
  134.             case 'X':
  135.                 printf("\n\\x escape sequence not supported yet");
  136.                 break;
  137.             default:
  138.                 printf("\nBad \\ escape sequence: %c",*S);
  139.                 *(D++)='\\';
  140.                 S--; //Compensate for below
  141.                 break;
  142.         }
  143.  
  144.         S++; //Skip at least one char from above; do here for compactness.
  145.  
  146.         //Copy up to next occurence or null, then loop again
  147.         while( (*S!='\\') && (*S!='\0') )
  148.             *(D++)=*(S++);
  149.     }
  150.  
  151.     return D-String;  //Return final parsed size of string
  152. }
  153.  
  154. //Byte shoving functions in #defines for speed
  155.  
  156. #define FRead(Buffer,BufferSize,File)\
  157. if( (BytesRead=fread(Buffer,1,BufferSize,File)) != BufferSize )\
  158. {\
  159.     printf(    "\nRead error.  Unexpected end-of-file?"\
  160.         "\nFRead reports %Xh of %Xh requested bytes read.",\
  161.         BytesRead,BufferSize);\
  162.     return RET_BadRead;\
  163. }
  164.  
  165. #define FWrite(Buffer,BufferSize,File)\
  166. if( (BytesWritten=fwrite(Buffer,1,BufferSize,File)) != BufferSize)\
  167.     printf(    "\nWrite error.  Out of disk space?  Error ignored."\
  168.         "\nFWrite reports %Xh of %Xh requested bytes written",\
  169.         BytesWritten,BufferSize);
  170.  
  171. #define    TransferBlock(Size)\
  172. {\
  173.  while(Size>=BufferSize)\
  174.  {\
  175.   FRead(Buffer,BufferSize,FileAFS)\
  176.   FWrite(Buffer,BufferSize,FileAFD)\
  177.   FilePos+=BufferSize;\
  178.   Size-=BufferSize;\
  179.   putchar('.');\
  180.  }\
  181.  if(Size)\
  182.  {\
  183.   FRead(Buffer,Size,FileAFS)\
  184.   FWrite(Buffer,Size,FileAFD)\
  185.   FilePos+=Size;\
  186.   putchar('.');\
  187.  }\
  188. }
  189.  
  190. int    ApplyHacks(FILE *FileHack,FILE *FileAFS,FILE *FileAFD,
  191.         unsigned char *Buffer,size_t BufferSize)
  192. {
  193.     size_t        BytesRead,BytesWritten;
  194.     unsigned char    HackLocStr[9],HackType,*c;
  195.     unsigned long    HackLoc,FileSize,FilePos=0,l;
  196.     unsigned int    NumHacks,NumHacksDone=0,i,j;
  197.  
  198.     if(HackScanf(FileHack,"%d\n",&NumHacks)!=1)
  199.     {
  200.         printf("Invalid Hack file.\n");
  201.         return RET_BadFile;
  202.     }
  203.     printf("Applying %u hack(s).",NumHacks);
  204.  
  205.     //Get length of file
  206.     fseek(FileAFS,0,SEEK_END);
  207.     FileSize=ftell(FileAFS);
  208.     fseek(FileAFS,0,SEEK_SET);
  209.  
  210.     while(NumHacksDone<NumHacks)
  211.     {
  212.         //Read in Hack Location
  213.         if(HackScanf(FileHack,"%8[0-9A-Fa-f]:%c:",&HackLocStr,&HackType)!=2)
  214.         {
  215.             printf(
  216. "\nUnexpected end-of-file or read error in Hack file");
  217.             break;
  218.         }
  219.         //Actual point -2 if resizing, to access length field
  220.         HackLoc=strtoul(HackLocStr,0,16) - (HackType=='R'?2:0);
  221.  
  222.         //Check for proper ordering
  223.         if(HackLoc<FilePos)
  224.         {
  225.             printf(
  226. "\nOut-of-order hack at %lXh.  Hacks must be in address order",
  227.                 HackLoc + (HackType=='R'?2:0));
  228.             break;
  229.         }
  230.  
  231.         //Copy up to the Hack point
  232.         l=HackLoc-FilePos;
  233.         TransferBlock(l);
  234.  
  235.         //Read in Hack String
  236.         c=Buffer;
  237.         while(    ( *(c++) = getc(FileHack) ) != '\n' &&
  238.             !feof(FileHack) );
  239.  
  240.         if(*(--c)!='\n') //Then the loop obviously terminated on EOF
  241.         {
  242.             printf(
  243. "\nUnexpected end-of-file or read error in Hack file");
  244.             break;
  245.         }
  246.         *c='\0'; //Null terminate
  247.         j=ParseCString(Buffer);
  248.  
  249.  
  250.         if(HackType=='R') //If resizing...
  251.         {
  252.             //Skip existing string in original
  253.             FRead(&i,sizeof(i),FileAFS)
  254.             FilePos+=sizeof(i)+i;
  255.             fseek(FileAFS,FilePos,SEEK_SET);
  256.  
  257.             //Warn if oversize
  258.             if(j>1024)
  259.                 printf(
  260. "\nWarning: Hack string at %lXh exceeds 1KB (%i bytes)"
  261. "\nThis might cause problems with OMF",
  262.                 //Compensate for internal -2
  263.                 HackLoc+2,j);
  264.  
  265.             //Put new string in
  266.             FWrite(&j,sizeof(j),FileAFD)
  267.             FWrite(Buffer,j,FileAFD)
  268.         }
  269.         else if (HackType=='O') //If overwriting...
  270.         {
  271.             //Skip existing string in original
  272.             FilePos+=j;
  273.             fseek(FileAFS,FilePos,SEEK_SET);
  274.  
  275.             //Put new string in
  276.             FWrite(Buffer,j,FileAFD)
  277.         }
  278.         else
  279.         {
  280.             printf("\nIgnoring bad hack type: %c",HackType);
  281.         }
  282.  
  283.         NumHacksDone++;
  284.     }
  285.  
  286.     //Copy rest of original file
  287.     if( (l=FileSize-FilePos) != 0 )
  288.         TransferBlock(l);
  289.  
  290.     printf("\n%u of %u hack(s) applied.\n",NumHacksDone,NumHacks);
  291.     return RET_Normal;
  292. }
  293.  
  294. int _cdecl    main(int argc,unsigned char *argv[])
  295. {
  296.     FILE        *FileHack,*FileAFS,*FileAFD;
  297.     unsigned char    *Buffer;
  298.     size_t        BufferSize;
  299.     int        i;
  300.     const char    *FileOpenError="Couldn't open file: %s\n";
  301.  
  302.  
  303.     puts(Version);
  304.     if(argc<4)
  305.     {
  306.         printf(
  307. "Usage: AFHack <Hack file> <Source AF file> <Destination AF file>\n"
  308.         );
  309.         exit(RET_BadParms);
  310.     }
  311.  
  312.     printf(    "Hack  : %s\n"
  313.         "AF Src: %s\n"
  314.         "AF Dst: %s\n",argv[1],argv[2],argv[3]);
  315.  
  316.     if(!(FileHack=fopen(argv[1],"rt")))
  317.     {
  318.         printf(FileOpenError,argv[1]);
  319.         return RET_BadFile;
  320.     }
  321.     if(!(FileAFS=fopen(argv[2],"rb")) )
  322.     {
  323.         printf(FileOpenError,argv[2]);
  324.         return RET_BadFile;
  325.     }
  326.     if(!(FileAFD=fopen(argv[3],"w+b")) )
  327.     {
  328.         printf(FileOpenError,argv[3]);
  329.         return RET_BadFile;
  330.     }
  331.  
  332.     //Allocate all available near memory as buffer
  333.     if(!(Buffer=(unsigned char *)malloc(BufferSize=coreleft())))
  334.     {
  335.         puts("Could not allocate available memory as buffer!\n");
  336.         return RET_OutOfMemory;
  337.     }
  338.     printf("Buffer: %u bytes.\n",BufferSize);
  339.  
  340.     i=ApplyHacks(FileHack,FileAFS,FileAFD,Buffer,BufferSize);
  341.  
  342.     free(Buffer);
  343.  
  344.     if(fclose(FileHack)|fclose(FileAFS)|fclose(FileAFD))
  345.     {
  346.         puts("Error closing file(s)!\n");
  347.         return RET_BadClose;
  348.     }
  349.  
  350.     return i;
  351. }
  352.